/*!
 * typeahead.js 0.11.1
 * https://github.com/twitter/typeahead.js
 * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
 */

(function(root, factory) {
    if (typeof define === "function" && define.amd) {
        define("typeahead.js",
            ["jquery"],
            function(a0) {
                return factory(a0);
            });
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"));
    } else {
        factory(jQuery);
    }
})(this,
    function($) {
        var _ = function() {
            "use strict";
            return {
                isMsie: function() {
                    return /(msie|trident)/i.test(navigator.userAgent)
                        ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]
                        : false;
                },
                isBlankString: function(str) {
                    return !str || /^\s*$/.test(str);
                },
                escapeRegExChars: function(str) {
                    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
                },
                isString: function(obj) {
                    return typeof obj === "string";
                },
                isNumber: function(obj) {
                    return typeof obj === "number";
                },
                isArray: $.isArray,
                isFunction: $.isFunction,
                isObject: $.isPlainObject,
                isUndefined: function(obj) {
                    return typeof obj === "undefined";
                },
                isElement: function(obj) {
                    return !!(obj && obj.nodeType === 1);
                },
                isJQuery: function(obj) {
                    return obj instanceof $;
                },
                toStr: function toStr(s) {
                    return _.isUndefined(s) || s === null ? "" : s + "";
                },
                bind: $.proxy,
                each: function(collection, cb) {
                    $.each(collection, reverseArgs);

                    function reverseArgs(index, value) {
                        return cb(value, index);
                    }
                },
                map: $.map,
                filter: $.grep,
                every: function(obj, test) {
                    var result = true;
                    if (!obj) {
                        return result;
                    }
                    $.each(obj,
                        function(key, val) {
                            if (!(result = test.call(null, val, key, obj))) {
                                return false;
                            }
                        });
                    return !!result;
                },
                some: function(obj, test) {
                    var result = false;
                    if (!obj) {
                        return result;
                    }
                    $.each(obj,
                        function(key, val) {
                            if (result = test.call(null, val, key, obj)) {
                                return false;
                            }
                        });
                    return !!result;
                },
                mixin: $.extend,
                identity: function(x) {
                    return x;
                },
                clone: function(obj) {
                    return $.extend(true, {}, obj);
                },
                getIdGenerator: function() {
                    var counter = 0;
                    return function() {
                        return counter++;
                    };
                },
                templatify: function templatify(obj) {
                    return $.isFunction(obj) ? obj : template;

                    function template() {
                        return String(obj);
                    }
                },
                defer: function(fn) {
                    setTimeout(fn, 0);
                },
                debounce: function(func, wait, immediate) {
                    var timeout, result;
                    return function() {
                        var context = this, args = arguments, later, callNow;
                        later = function() {
                            timeout = null;
                            if (!immediate) {
                                result = func.apply(context, args);
                            }
                        };
                        callNow = immediate && !timeout;
                        clearTimeout(timeout);
                        timeout = setTimeout(later, wait);
                        if (callNow) {
                            result = func.apply(context, args);
                        }
                        return result;
                    };
                },
                throttle: function(func, wait) {
                    var context, args, timeout, result, previous, later;
                    previous = 0;
                    later = function() {
                        previous = new Date();
                        timeout = null;
                        result = func.apply(context, args);
                    };
                    return function() {
                        var now = new Date(), remaining = wait - (now - previous);
                        context = this;
                        args = arguments;
                        if (remaining <= 0) {
                            clearTimeout(timeout);
                            timeout = null;
                            previous = now;
                            result = func.apply(context, args);
                        } else if (!timeout) {
                            timeout = setTimeout(later, remaining);
                        }
                        return result;
                    };
                },
                stringify: function(val) {
                    return _.isString(val) ? val : JSON.stringify(val);
                },
                noop: function() {}
            };
        }();
        var WWW = function() {
            "use strict";
            var defaultClassNames = {
                wrapper: "twitter-typeahead",
                input: "tt-input",
                hint: "tt-hint",
                menu: "tt-menu",
                dataset: "tt-dataset",
                suggestion: "tt-suggestion",
                selectable: "tt-selectable",
                empty: "tt-empty",
                open: "tt-open",
                cursor: "tt-cursor",
                highlight: "tt-highlight"
            };
            return build;

            function build(o) {
                var www, classes;
                classes = _.mixin({}, defaultClassNames, o);
                www = {
                    css: buildCss(),
                    classes: classes,
                    html: buildHtml(classes),
                    selectors: buildSelectors(classes)
                };
                return {
                    css: www.css,
                    html: www.html,
                    classes: www.classes,
                    selectors: www.selectors,
                    mixin: function(o) {
                        _.mixin(o, www);
                    }
                };
            }

            function buildHtml(c) {
                return {
                    wrapper: '<span class="' + c.wrapper + '"></span>',
                    menu: '<div class="' + c.menu + '"></div>'
                };
            }

            function buildSelectors(classes) {
                var selectors = {};
                _.each(classes,
                    function(v, k) {
                        selectors[k] = "." + v;
                    });
                return selectors;
            }

            function buildCss() {
                var css = {
                    wrapper: {
                        position: "relative",
                        display: "inline-block"
                    },
                    hint: {
                        position: "absolute",
                        top: "0",
                        left: "0",
                        borderColor: "transparent",
                        boxShadow: "none",
                        opacity: "1"
                    },
                    input: {
                        position: "relative",
                        verticalAlign: "top",
                        backgroundColor: "transparent"
                    },
                    inputWithNoHint: {
                        position: "relative",
                        verticalAlign: "top"
                    },
                    menu: {
                        position: "absolute",
                        top: "100%",
                        left: "0",
                        zIndex: "100",
                        display: "none"
                    },
                    ltr: {
                        left: "0",
                        right: "auto"
                    },
                    rtl: {
                        left: "auto",
                        right: " 0"
                    }
                };
                if (_.isMsie()) {
                    _.mixin(css.input,
                        {
                            backgroundImage:
                                "url()"
                        });
                }
                return css;
            }
        }();
        var EventBus = function() {
            "use strict";
            var namespace, deprecationMap;
            namespace = "typeahead:";
            deprecationMap = {
                render: "rendered",
                cursorchange: "cursorchanged",
                select: "selected",
                autocomplete: "autocompleted"
            };

            function EventBus(o) {
                if (!o || !o.el) {
                    $.error("EventBus initialized without el");
                }
                this.$el = $(o.el);
            }

            _.mixin(EventBus.prototype,
                {
                    _trigger: function(type, args) {
                        var $e;
                        $e = $.Event(namespace + type);
                        (args = args || []).unshift($e);
                        this.$el.trigger.apply(this.$el, args);
                        return $e;
                    },
                    before: function(type) {
                        var args, $e;
                        args = [].slice.call(arguments, 1);
                        $e = this._trigger("before" + type, args);
                        return $e.isDefaultPrevented();
                    },
                    trigger: function(type) {
                        var deprecatedType;
                        this._trigger(type, [].slice.call(arguments, 1));
                        if (deprecatedType = deprecationMap[type]) {
                            this._trigger(deprecatedType, [].slice.call(arguments, 1));
                        }
                    }
                });
            return EventBus;
        }();
        var EventEmitter = function() {
            "use strict";
            var splitter = /\s+/, nextTick = getNextTick();
            return {
                onSync: onSync,
                onAsync: onAsync,
                off: off,
                trigger: trigger
            };

            function on(method, types, cb, context) {
                var type;
                if (!cb) {
                    return this;
                }
                types = types.split(splitter);
                cb = context ? bindContext(cb, context) : cb;
                this._callbacks = this._callbacks || {};
                while (type = types.shift()) {
                    this._callbacks[type] = this._callbacks[type] ||
                    {
                        sync: [],
                        async: []
                    };
                    this._callbacks[type][method].push(cb);
                }
                return this;
            }

            function onAsync(types, cb, context) {
                return on.call(this, "async", types, cb, context);
            }

            function onSync(types, cb, context) {
                return on.call(this, "sync", types, cb, context);
            }

            function off(types) {
                var type;
                if (!this._callbacks) {
                    return this;
                }
                types = types.split(splitter);
                while (type = types.shift()) {
                    delete this._callbacks[type];
                }
                return this;
            }

            function trigger(types) {
                var type, callbacks, args, syncFlush, asyncFlush;
                if (!this._callbacks) {
                    return this;
                }
                types = types.split(splitter);
                args = [].slice.call(arguments, 1);
                while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
                    syncFlush = getFlush(callbacks.sync, this, [type].concat(args));
                    asyncFlush = getFlush(callbacks.async, this, [type].concat(args));
                    syncFlush() && nextTick(asyncFlush);
                }
                return this;
            }

            function getFlush(callbacks, context, args) {
                return flush;

                function flush() {
                    var cancelled;
                    for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
                        cancelled = callbacks[i].apply(context, args) === false;
                    }
                    return !cancelled;
                }
            }

            function getNextTick() {
                var nextTickFn;
                if (window.setImmediate) {
                    nextTickFn = function nextTickSetImmediate(fn) {
                        setImmediate(function() {
                            fn();
                        });
                    };
                } else {
                    nextTickFn = function nextTickSetTimeout(fn) {
                        setTimeout(function() {
                                fn();
                            },
                            0);
                    };
                }
                return nextTickFn;
            }

            function bindContext(fn, context) {
                return fn.bind
                    ? fn.bind(context)
                    : function() {
                        fn.apply(context, [].slice.call(arguments, 0));
                    };
            }
        }();
        var highlight = function(doc) {
            "use strict";
            var defaults = {
                node: null,
                pattern: null,
                tagName: "strong",
                className: null,
                wordsOnly: false,
                caseSensitive: false
            };
            return function hightlight(o) {
                var regex;
                o = _.mixin({}, defaults, o);
                if (!o.node || !o.pattern) {
                    return;
                }
                o.pattern = _.isArray(o.pattern) ? o.pattern : [o.pattern];
                regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
                traverse(o.node, hightlightTextNode);

                function hightlightTextNode(textNode) {
                    var match, patternNode, wrapperNode;
                    if (match = regex.exec(textNode.data)) {
                        wrapperNode = doc.createElement(o.tagName);
                        o.className && (wrapperNode.className = o.className);
                        patternNode = textNode.splitText(match.index);
                        patternNode.splitText(match[0].length);
                        wrapperNode.appendChild(patternNode.cloneNode(true));
                        textNode.parentNode.replaceChild(wrapperNode, patternNode);
                    }
                    return !!match;
                }

                function traverse(el, hightlightTextNode) {
                    var childNode, TEXT_NODE_TYPE = 3;
                    for (var i = 0; i < el.childNodes.length; i++) {
                        childNode = el.childNodes[i];
                        if (childNode.nodeType === TEXT_NODE_TYPE) {
                            i += hightlightTextNode(childNode) ? 1 : 0;
                        } else {
                            traverse(childNode, hightlightTextNode);
                        }
                    }
                }
            };

            function getRegex(patterns, caseSensitive, wordsOnly) {
                var escapedPatterns = [], regexStr;
                for (var i = 0, len = patterns.length; i < len; i++) {
                    escapedPatterns.push(_.escapeRegExChars(patterns[i]));
                }
                regexStr = wordsOnly
                    ? "\\b(" + escapedPatterns.join("|") + ")\\b"
                    : "(" + escapedPatterns.join("|") + ")";
                return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
            }
        }(window.document);
        var Input = function() {
            "use strict";
            var specialKeyCodeMap;
            specialKeyCodeMap = {
                9: "tab",
                27: "esc",
                37: "left",
                39: "right",
                13: "enter",
                38: "up",
                40: "down"
            };

            function Input(o, www) {
                o = o || {};
                if (!o.input) {
                    $.error("input is missing");
                }
                www.mixin(this);
                this.$hint = $(o.hint);
                this.$input = $(o.input);
                this.query = this.$input.val();
                this.queryWhenFocused = this.hasFocus() ? this.query : null;
                this.$overflowHelper = buildOverflowHelper(this.$input);
                this._checkLanguageDirection();
                if (this.$hint.length === 0) {
                    this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
                }
            }

            Input.normalizeQuery = function(str) {
                return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
            };
            _.mixin(Input.prototype,
                EventEmitter,
                {
                    _onBlur: function onBlur() {
                        this.resetInputValue();
                        this.trigger("blurred");
                    },
                    _onFocus: function onFocus() {
                        this.queryWhenFocused = this.query;
                        this.trigger("focused");
                    },
                    _onKeydown: function onKeydown($e) {
                        var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
                        this._managePreventDefault(keyName, $e);
                        if (keyName && this._shouldTrigger(keyName, $e)) {
                            this.trigger(keyName + "Keyed", $e);
                        }
                    },
                    _onInput: function onInput() {
                        this._setQuery(this.getInputValue());
                        this.clearHintIfInvalid();
                        this._checkLanguageDirection();
                    },
                    _managePreventDefault: function managePreventDefault(keyName, $e) {
                        var preventDefault;
                        switch (keyName) {
                        case "up":
                        case "down":
                            preventDefault = !withModifier($e);
                            break;

                        default:
                            preventDefault = false;
                        }
                        preventDefault && $e.preventDefault();
                    },
                    _shouldTrigger: function shouldTrigger(keyName, $e) {
                        var trigger;
                        switch (keyName) {
                        case "tab":
                            trigger = !withModifier($e);
                            break;

                        default:
                            trigger = true;
                        }
                        return trigger;
                    },
                    _checkLanguageDirection: function checkLanguageDirection() {
                        var dir = (this.$input.css("direction") || "ltr").toLowerCase();
                        if (this.dir !== dir) {
                            this.dir = dir;
                            this.$hint.attr("dir", dir);
                            this.trigger("langDirChanged", dir);
                        }
                    },
                    _setQuery: function setQuery(val, silent) {
                        var areEquivalent, hasDifferentWhitespace;
                        areEquivalent = areQueriesEquivalent(val, this.query);
                        hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
                        this.query = val;
                        if (!silent && !areEquivalent) {
                            this.trigger("queryChanged", this.query);
                        } else if (!silent && hasDifferentWhitespace) {
                            this.trigger("whitespaceChanged", this.query);
                        }
                    },
                    bind: function() {
                        var that = this, onBlur, onFocus, onKeydown, onInput;
                        onBlur = _.bind(this._onBlur, this);
                        onFocus = _.bind(this._onFocus, this);
                        onKeydown = _.bind(this._onKeydown, this);
                        onInput = _.bind(this._onInput, this);
                        this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
                        if (!_.isMsie() || _.isMsie() > 9) {
                            this.$input.on("input.tt", onInput);
                        } else {
                            this.$input.on("keydown.tt keypress.tt cut.tt paste.tt",
                                function($e) {
                                    if (specialKeyCodeMap[$e.which || $e.keyCode]) {
                                        return;
                                    }
                                    _.defer(_.bind(that._onInput, that, $e));
                                });
                        }
                        return this;
                    },
                    focus: function focus() {
                        this.$input.focus();
                    },
                    blur: function blur() {
                        this.$input.blur();
                    },
                    getLangDir: function getLangDir() {
                        return this.dir;
                    },
                    getQuery: function getQuery() {
                        return this.query || "";
                    },
                    setQuery: function setQuery(val, silent) {
                        this.setInputValue(val);
                        this._setQuery(val, silent);
                    },
                    hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
                        return this.query !== this.queryWhenFocused;
                    },
                    getInputValue: function getInputValue() {
                        return this.$input.val();
                    },
                    setInputValue: function setInputValue(value) {
                        this.$input.val(value);
                        this.clearHintIfInvalid();
                        this._checkLanguageDirection();
                    },
                    resetInputValue: function resetInputValue() {
                        this.setInputValue(this.query);
                    },
                    getHint: function getHint() {
                        return this.$hint.val();
                    },
                    setHint: function setHint(value) {
                        this.$hint.val(value);
                    },
                    clearHint: function clearHint() {
                        this.setHint("");
                    },
                    clearHintIfInvalid: function clearHintIfInvalid() {
                        var val, hint, valIsPrefixOfHint, isValid;
                        val = this.getInputValue();
                        hint = this.getHint();
                        valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
                        isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
                        !isValid && this.clearHint();
                    },
                    hasFocus: function hasFocus() {
                        return this.$input.is(":focus");
                    },
                    hasOverflow: function hasOverflow() {
                        var constraint = this.$input.width() - 2;
                        this.$overflowHelper.text(this.getInputValue());
                        return this.$overflowHelper.width() >= constraint;
                    },
                    isCursorAtEnd: function() {
                        var valueLength, selectionStart, range;
                        valueLength = this.$input.val().length;
                        selectionStart = this.$input[0].selectionStart;
                        if (_.isNumber(selectionStart)) {
                            return selectionStart === valueLength;
                        } else if (document.selection) {
                            range = document.selection.createRange();
                            range.moveStart("character", -valueLength);
                            return valueLength === range.text.length;
                        }
                        return true;
                    },
                    destroy: function destroy() {
                        this.$hint.off(".tt");
                        this.$input.off(".tt");
                        this.$overflowHelper.remove();
                        this.$hint = this.$input = this.$overflowHelper = $("<div>");
                    }
                });
            return Input;

            function buildOverflowHelper($input) {
                return $('<pre aria-hidden="true"></pre>').css({
                    position: "absolute",
                    visibility: "hidden",
                    whiteSpace: "pre",
                    fontFamily: $input.css("font-family"),
                    fontSize: $input.css("font-size"),
                    fontStyle: $input.css("font-style"),
                    fontVariant: $input.css("font-variant"),
                    fontWeight: $input.css("font-weight"),
                    wordSpacing: $input.css("word-spacing"),
                    letterSpacing: $input.css("letter-spacing"),
                    textIndent: $input.css("text-indent"),
                    textRendering: $input.css("text-rendering"),
                    textTransform: $input.css("text-transform")
                }).insertAfter($input);
            }

            function areQueriesEquivalent(a, b) {
                return Input.normalizeQuery(a) === Input.normalizeQuery(b);
            }

            function withModifier($e) {
                return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
            }
        }();
        var Dataset = function() {
            "use strict";
            var keys, nameGenerator;
            keys = {
                val: "tt-selectable-display",
                obj: "tt-selectable-object"
            };
            nameGenerator = _.getIdGenerator();

            function Dataset(o, www) {
                o = o || {};
                o.templates = o.templates || {};
                o.templates.notFound = o.templates.notFound || o.templates.empty;
                if (!o.source) {
                    $.error("missing source");
                }
                if (!o.node) {
                    $.error("missing node");
                }
                if (o.name && !isValidName(o.name)) {
                    $.error("invalid dataset name: " + o.name);
                }
                www.mixin(this);
                this.highlight = !!o.highlight;
                this.name = o.name || nameGenerator();
                this.limit = o.limit || 5;
                this.displayFn = getDisplayFn(o.display || o.displayKey);
                this.templates = getTemplates(o.templates, this.displayFn);
                this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
                this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
                this._resetLastSuggestion();
                this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
            }

            Dataset.extractData = function extractData(el) {
                var $el = $(el);
                if ($el.data(keys.obj)) {
                    return {
                        val: $el.data(keys.val) || "",
                        obj: $el.data(keys.obj) || null
                    };
                }
                return null;
            };
            _.mixin(Dataset.prototype,
                EventEmitter,
                {
                    _overwrite: function overwrite(query, suggestions) {
                        suggestions = suggestions || [];
                        if (suggestions.length) {
                            this._renderSuggestions(query, suggestions);
                        } else if (this.async && this.templates.pending) {
                            this._renderPending(query);
                        } else if (!this.async && this.templates.notFound) {
                            this._renderNotFound(query);
                        } else {
                            this._empty();
                        }
                        this.trigger("rendered", this.name, suggestions, false);
                    },
                    _append: function append(query, suggestions) {
                        suggestions = suggestions || [];
                        if (suggestions.length && this.$lastSuggestion.length) {
                            this._appendSuggestions(query, suggestions);
                        } else if (suggestions.length) {
                            this._renderSuggestions(query, suggestions);
                        } else if (!this.$lastSuggestion.length && this.templates.notFound) {
                            this._renderNotFound(query);
                        }
                        this.trigger("rendered", this.name, suggestions, true);
                    },
                    _renderSuggestions: function renderSuggestions(query, suggestions) {
                        var $fragment;
                        $fragment = this._getSuggestionsFragment(query, suggestions);
                        this.$lastSuggestion = $fragment.children().last();
                        this.$el.html($fragment).prepend(this._getHeader(query, suggestions))
                            .append(this._getFooter(query, suggestions));
                    },
                    _appendSuggestions: function appendSuggestions(query, suggestions) {
                        var $fragment, $lastSuggestion;
                        $fragment = this._getSuggestionsFragment(query, suggestions);
                        $lastSuggestion = $fragment.children().last();
                        this.$lastSuggestion.after($fragment);
                        this.$lastSuggestion = $lastSuggestion;
                    },
                    _renderPending: function renderPending(query) {
                        var template = this.templates.pending;
                        this._resetLastSuggestion();
                        template &&
                            this.$el.html(template({
                                query: query,
                                dataset: this.name
                            }));
                    },
                    _renderNotFound: function renderNotFound(query) {
                        var template = this.templates.notFound;
                        this._resetLastSuggestion();
                        template &&
                            this.$el.html(template({
                                query: query,
                                dataset: this.name
                            }));
                    },
                    _empty: function empty() {
                        this.$el.empty();
                        this._resetLastSuggestion();
                    },
                    _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
                        var that = this, fragment;
                        fragment = document.createDocumentFragment();
                        _.each(suggestions,
                            function getSuggestionNode(suggestion) {
                                var $el, context;
                                context = that._injectQuery(query, suggestion);
                                $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion)
                                    .data(keys.val, that.displayFn(suggestion))
                                    .addClass(that.classes.suggestion + " " + that.classes.selectable);
                                fragment.appendChild($el[0]);
                            });
                        this.highlight &&
                            highlight({
                                className: this.classes.highlight,
                                node: fragment,
                                pattern: query
                            });
                        return $(fragment);
                    },
                    _getFooter: function getFooter(query, suggestions) {
                        return this.templates.footer
                            ? this.templates.footer({
                                query: query,
                                suggestions: suggestions,
                                dataset: this.name
                            })
                            : null;
                    },
                    _getHeader: function getHeader(query, suggestions) {
                        return this.templates.header
                            ? this.templates.header({
                                query: query,
                                suggestions: suggestions,
                                dataset: this.name
                            })
                            : null;
                    },
                    _resetLastSuggestion: function resetLastSuggestion() {
                        this.$lastSuggestion = $();
                    },
                    _injectQuery: function injectQuery(query, obj) {
                        return _.isObject(obj)
                            ? _.mixin({
                                    _query: query
                                },
                                obj)
                            : obj;
                    },
                    update: function update(query) {
                        var that = this, canceled = false, syncCalled = false, rendered = 0;
                        this.cancel();
                        this.cancel = function cancel() {
                            canceled = true;
                            that.cancel = $.noop;
                            that.async && that.trigger("asyncCanceled", query);
                        };
                        this.source(query, sync, async);
                        !syncCalled && sync([]);

                        function sync(suggestions) {
                            if (syncCalled) {
                                return;
                            }
                            syncCalled = true;
                            suggestions = (suggestions || []).slice(0, that.limit);
                            rendered = suggestions.length;
                            that._overwrite(query, suggestions);
                            if (rendered < that.limit && that.async) {
                                that.trigger("asyncRequested", query);
                            }
                        }

                        function async(suggestions) {
                            suggestions = suggestions || [];
                            if (!canceled && rendered < that.limit) {
                                that.cancel = $.noop;
                                rendered += suggestions.length;
                                that._append(query, suggestions.slice(0, that.limit - rendered));
                                that.async && that.trigger("asyncReceived", query);
                            }
                        }
                    },
                    cancel: $.noop,
                    clear: function clear() {
                        this._empty();
                        this.cancel();
                        this.trigger("cleared");
                    },
                    isEmpty: function isEmpty() {
                        return this.$el.is(":empty");
                    },
                    destroy: function destroy() {
                        this.$el = $("<div>");
                    }
                });
            return Dataset;

            function getDisplayFn(display) {
                display = display || _.stringify;
                return _.isFunction(display) ? display : displayFn;

                function displayFn(obj) {
                    return obj[display];
                }
            }

            function getTemplates(templates, displayFn) {
                return {
                    notFound: templates.notFound && _.templatify(templates.notFound),
                    pending: templates.pending && _.templatify(templates.pending),
                    header: templates.header && _.templatify(templates.header),
                    footer: templates.footer && _.templatify(templates.footer),
                    suggestion: templates.suggestion || suggestionTemplate
                };

                function suggestionTemplate(context) {
                    return $("<div>").text(displayFn(context));
                }
            }

            function isValidName(str) {
                return /^[_a-zA-Z0-9-]+$/.test(str);
            }
        }();
        var Menu = function() {
            "use strict";

            function Menu(o, www) {
                var that = this;
                o = o || {};
                if (!o.node) {
                    $.error("node is required");
                }
                www.mixin(this);
                this.$node = $(o.node);
                this.query = null;
                this.datasets = _.map(o.datasets, initializeDataset);

                function initializeDataset(oDataset) {
                    var node = that.$node.find(oDataset.node).first();
                    oDataset.node = node.length ? node : $("<div>").appendTo(that.$node);
                    return new Dataset(oDataset, www);
                }
            }

            _.mixin(Menu.prototype,
                EventEmitter,
                {
                    _onSelectableClick: function onSelectableClick($e) {
                        this.trigger("selectableClicked", $($e.currentTarget));
                    },
                    _onRendered: function onRendered(type, dataset, suggestions, async) {
                        this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
                        this.trigger("datasetRendered", dataset, suggestions, async);
                    },
                    _onCleared: function onCleared() {
                        this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
                        this.trigger("datasetCleared");
                    },
                    _propagate: function propagate() {
                        this.trigger.apply(this, arguments);
                    },
                    _allDatasetsEmpty: function allDatasetsEmpty() {
                        return _.every(this.datasets, isDatasetEmpty);

                        function isDatasetEmpty(dataset) {
                            return dataset.isEmpty();
                        }
                    },
                    _getSelectables: function getSelectables() {
                        return this.$node.find(this.selectors.selectable);
                    },
                    _removeCursor: function _removeCursor() {
                        var $selectable = this.getActiveSelectable();
                        $selectable && $selectable.removeClass(this.classes.cursor);
                    },
                    _ensureVisible: function ensureVisible($el) {
                        var elTop, elBottom, nodeScrollTop, nodeHeight;
                        elTop = $el.position().top;
                        elBottom = elTop + $el.outerHeight(true);
                        nodeScrollTop = this.$node.scrollTop();
                        nodeHeight =
                            this.$node.height() +
                            parseInt(this.$node.css("paddingTop"), 10) +
                            parseInt(this.$node.css("paddingBottom"), 10);
                        if (elTop < 0) {
                            this.$node.scrollTop(nodeScrollTop + elTop);
                        } else if (nodeHeight < elBottom) {
                            this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
                        }
                    },
                    bind: function() {
                        var that = this, onSelectableClick;
                        onSelectableClick = _.bind(this._onSelectableClick, this);
                        this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
                        _.each(this.datasets,
                            function(dataset) {
                                dataset.onSync("asyncRequested", that._propagate, that)
                                    .onSync("asyncCanceled", that._propagate, that)
                                    .onSync("asyncReceived", that._propagate, that)
                                    .onSync("rendered", that._onRendered, that)
                                    .onSync("cleared", that._onCleared, that);
                            });
                        return this;
                    },
                    isOpen: function isOpen() {
                        return this.$node.hasClass(this.classes.open);
                    },
                    open: function open() {
                        this.$node.addClass(this.classes.open);
                    },
                    close: function close() {
                        this.$node.removeClass(this.classes.open);
                        this._removeCursor();
                    },
                    setLanguageDirection: function setLanguageDirection(dir) {
                        this.$node.attr("dir", dir);
                    },
                    selectableRelativeToCursor: function selectableRelativeToCursor(delta) {
                        var $selectables, $oldCursor, oldIndex, newIndex;
                        $oldCursor = this.getActiveSelectable();
                        $selectables = this._getSelectables();
                        oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;
                        newIndex = oldIndex + delta;
                        newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;
                        newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;
                        return newIndex === -1 ? null : $selectables.eq(newIndex);
                    },
                    setCursor: function setCursor($selectable) {
                        this._removeCursor();
                        if ($selectable = $selectable && $selectable.first()) {
                            $selectable.addClass(this.classes.cursor);
                            this._ensureVisible($selectable);
                        }
                    },
                    getSelectableData: function getSelectableData($el) {
                        return $el && $el.length ? Dataset.extractData($el) : null;
                    },
                    getActiveSelectable: function getActiveSelectable() {
                        var $selectable = this._getSelectables().filter(this.selectors.cursor).first();
                        return $selectable.length ? $selectable : null;
                    },
                    getTopSelectable: function getTopSelectable() {
                        var $selectable = this._getSelectables().first();
                        return $selectable.length ? $selectable : null;
                    },
                    update: function update(query) {
                        var isValidUpdate = query !== this.query;
                        if (isValidUpdate) {
                            this.query = query;
                            _.each(this.datasets, updateDataset);
                        }
                        return isValidUpdate;

                        function updateDataset(dataset) {
                            dataset.update(query);
                        }
                    },
                    empty: function empty() {
                        _.each(this.datasets, clearDataset);
                        this.query = null;
                        this.$node.addClass(this.classes.empty);

                        function clearDataset(dataset) {
                            dataset.clear();
                        }
                    },
                    destroy: function destroy() {
                        this.$node.off(".tt");
                        this.$node = $("<div>");
                        _.each(this.datasets, destroyDataset);

                        function destroyDataset(dataset) {
                            dataset.destroy();
                        }
                    }
                });
            return Menu;
        }();
        var DefaultMenu = function() {
            "use strict";
            var s = Menu.prototype;

            function DefaultMenu() {
                Menu.apply(this, [].slice.call(arguments, 0));
            }

            _.mixin(DefaultMenu.prototype,
                Menu.prototype,
                {
                    open: function open() {
                        !this._allDatasetsEmpty() && this._show();
                        return s.open.apply(this, [].slice.call(arguments, 0));
                    },
                    close: function close() {
                        this._hide();
                        return s.close.apply(this, [].slice.call(arguments, 0));
                    },
                    _onRendered: function onRendered() {
                        if (this._allDatasetsEmpty()) {
                            this._hide();
                        } else {
                            this.isOpen() && this._show();
                        }
                        return s._onRendered.apply(this, [].slice.call(arguments, 0));
                    },
                    _onCleared: function onCleared() {
                        if (this._allDatasetsEmpty()) {
                            this._hide();
                        } else {
                            this.isOpen() && this._show();
                        }
                        return s._onCleared.apply(this, [].slice.call(arguments, 0));
                    },
                    setLanguageDirection: function setLanguageDirection(dir) {
                        this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl);
                        return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));
                    },
                    _hide: function hide() {
                        this.$node.hide();
                    },
                    _show: function show() {
                        this.$node.css("display", "block");
                    }
                });
            return DefaultMenu;
        }();
        var Typeahead = function() {
            "use strict";

            function Typeahead(o, www) {
                var onFocused,
                    onBlurred,
                    onEnterKeyed,
                    onTabKeyed,
                    onEscKeyed,
                    onUpKeyed,
                    onDownKeyed,
                    onLeftKeyed,
                    onRightKeyed,
                    onQueryChanged,
                    onWhitespaceChanged;
                o = o || {};
                if (!o.input) {
                    $.error("missing input");
                }
                if (!o.menu) {
                    $.error("missing menu");
                }
                if (!o.eventBus) {
                    $.error("missing event bus");
                }
                www.mixin(this);
                this.eventBus = o.eventBus;
                this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
                this.input = o.input;
                this.menu = o.menu;
                this.enabled = true;
                this.active = false;
                this.input.hasFocus() && this.activate();
                this.dir = this.input.getLangDir();
                this._hacks();
                this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this)
                    .onSync("asyncRequested", this._onAsyncRequested, this)
                    .onSync("asyncCanceled", this._onAsyncCanceled, this)
                    .onSync("asyncReceived", this._onAsyncReceived, this)
                    .onSync("datasetRendered", this._onDatasetRendered, this)
                    .onSync("datasetCleared", this._onDatasetCleared, this);
                onFocused = c(this, "activate", "open", "_onFocused");
                onBlurred = c(this, "deactivate", "_onBlurred");
                onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed");
                onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed");
                onEscKeyed = c(this, "isActive", "_onEscKeyed");
                onUpKeyed = c(this, "isActive", "open", "_onUpKeyed");
                onDownKeyed = c(this, "isActive", "open", "_onDownKeyed");
                onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed");
                onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed");
                onQueryChanged = c(this, "_openIfActive", "_onQueryChanged");
                onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged");
                this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this)
                    .onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this)
                    .onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this)
                    .onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this)
                    .onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this)
                    .onSync("whitespaceChanged", onWhitespaceChanged, this)
                    .onSync("langDirChanged", this._onLangDirChanged, this);
            }

            _.mixin(Typeahead.prototype,
                {
                    _hacks: function hacks() {
                        var $input, $menu;
                        $input = this.input.$input || $("<div>");
                        $menu = this.menu.$node || $("<div>");
                        $input.on("blur.tt",
                            function($e) {
                                var active, isActive, hasActive;
                                active = document.activeElement;
                                isActive = $menu.is(active);
                                hasActive = $menu.has(active).length > 0;
                                if (_.isMsie() && (isActive || hasActive)) {
                                    $e.preventDefault();
                                    $e.stopImmediatePropagation();
                                    _.defer(function() {
                                        $input.focus();
                                    });
                                }
                            });
                        $menu.on("mousedown.tt",
                            function($e) {
                                $e.preventDefault();
                            });
                    },
                    _onSelectableClicked: function onSelectableClicked(type, $el) {
                        this.select($el);
                    },
                    _onDatasetCleared: function onDatasetCleared() {
                        this._updateHint();
                    },
                    _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
                        this._updateHint();
                        this.eventBus.trigger("render", suggestions, async, dataset);
                    },
                    _onAsyncRequested: function onAsyncRequested(type, dataset, query) {
                        this.eventBus.trigger("asyncrequest", query, dataset);
                    },
                    _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
                        this.eventBus.trigger("asynccancel", query, dataset);
                    },
                    _onAsyncReceived: function onAsyncReceived(type, dataset, query) {
                        this.eventBus.trigger("asyncreceive", query, dataset);
                    },
                    _onFocused: function onFocused() {
                        this._minLengthMet() && this.menu.update(this.input.getQuery());
                    },
                    _onBlurred: function onBlurred() {
                        if (this.input.hasQueryChangedSinceLastFocus()) {
                            this.eventBus.trigger("change", this.input.getQuery());
                        }
                    },
                    _onEnterKeyed: function onEnterKeyed(type, $e) {
                        var $selectable;
                        if ($selectable = this.menu.getActiveSelectable()) {
                            this.select($selectable) && $e.preventDefault();
                        }
                    },
                    _onTabKeyed: function onTabKeyed(type, $e) {
                        var $selectable;
                        if ($selectable = this.menu.getActiveSelectable()) {
                            this.select($selectable) && $e.preventDefault();
                        } else if ($selectable = this.menu.getTopSelectable()) {
                            this.autocomplete($selectable) && $e.preventDefault();
                        }
                    },
                    _onEscKeyed: function onEscKeyed() {
                        this.close();
                    },
                    _onUpKeyed: function onUpKeyed() {
                        this.moveCursor(-1);
                    },
                    _onDownKeyed: function onDownKeyed() {
                        this.moveCursor(+1);
                    },
                    _onLeftKeyed: function onLeftKeyed() {
                        if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
                            this.autocomplete(this.menu.getTopSelectable());
                        }
                    },
                    _onRightKeyed: function onRightKeyed() {
                        if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
                            this.autocomplete(this.menu.getTopSelectable());
                        }
                    },
                    _onQueryChanged: function onQueryChanged(e, query) {
                        this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
                    },
                    _onWhitespaceChanged: function onWhitespaceChanged() {
                        this._updateHint();
                    },
                    _onLangDirChanged: function onLangDirChanged(e, dir) {
                        if (this.dir !== dir) {
                            this.dir = dir;
                            this.menu.setLanguageDirection(dir);
                        }
                    },
                    _openIfActive: function openIfActive() {
                        this.isActive() && this.open();
                    },
                    _minLengthMet: function minLengthMet(query) {
                        query = _.isString(query) ? query : this.input.getQuery() || "";
                        return query.length >= this.minLength;
                    },
                    _updateHint: function updateHint() {
                        var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
                        $selectable = this.menu.getTopSelectable();
                        data = this.menu.getSelectableData($selectable);
                        val = this.input.getInputValue();
                        if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
                            query = Input.normalizeQuery(val);
                            escapedQuery = _.escapeRegExChars(query);
                            frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
                            match = frontMatchRegEx.exec(data.val);
                            match && this.input.setHint(val + match[1]);
                        } else {
                            this.input.clearHint();
                        }
                    },
                    isEnabled: function isEnabled() {
                        return this.enabled;
                    },
                    enable: function enable() {
                        this.enabled = true;
                    },
                    disable: function disable() {
                        this.enabled = false;
                    },
                    isActive: function isActive() {
                        return this.active;
                    },
                    activate: function activate() {
                        if (this.isActive()) {
                            return true;
                        } else if (!this.isEnabled() || this.eventBus.before("active")) {
                            return false;
                        } else {
                            this.active = true;
                            this.eventBus.trigger("active");
                            return true;
                        }
                    },
                    deactivate: function deactivate() {
                        if (!this.isActive()) {
                            return true;
                        } else if (this.eventBus.before("idle")) {
                            return false;
                        } else {
                            this.active = false;
                            this.close();
                            this.eventBus.trigger("idle");
                            return true;
                        }
                    },
                    isOpen: function isOpen() {
                        return this.menu.isOpen();
                    },
                    open: function open() {
                        if (!this.isOpen() && !this.eventBus.before("open")) {
                            this.menu.open();
                            this._updateHint();
                            this.eventBus.trigger("open");
                        }
                        return this.isOpen();
                    },
                    close: function close() {
                        if (this.isOpen() && !this.eventBus.before("close")) {
                            this.menu.close();
                            this.input.clearHint();
                            this.input.resetInputValue();
                            this.eventBus.trigger("close");
                        }
                        return !this.isOpen();
                    },
                    setVal: function setVal(val) {
                        this.input.setQuery(_.toStr(val));
                    },
                    getVal: function getVal() {
                        return this.input.getQuery();
                    },
                    select: function select($selectable) {
                        var data = this.menu.getSelectableData($selectable);
                        if (data && !this.eventBus.before("select", data.obj)) {
                            this.input.setQuery(data.val, true);
                            this.eventBus.trigger("select", data.obj);
                            this.close();
                            return true;
                        }
                        return false;
                    },
                    autocomplete: function autocomplete($selectable) {
                        var query, data, isValid;
                        query = this.input.getQuery();
                        data = this.menu.getSelectableData($selectable);
                        isValid = data && query !== data.val;
                        if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
                            this.input.setQuery(data.val);
                            this.eventBus.trigger("autocomplete", data.obj);
                            return true;
                        }
                        return false;
                    },
                    moveCursor: function moveCursor(delta) {
                        var query, $candidate, data, payload, cancelMove;
                        query = this.input.getQuery();
                        $candidate = this.menu.selectableRelativeToCursor(delta);
                        data = this.menu.getSelectableData($candidate);
                        payload = data ? data.obj : null;
                        cancelMove = this._minLengthMet() && this.menu.update(query);
                        if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
                            this.menu.setCursor($candidate);
                            if (data) {
                                this.input.setInputValue(data.val);
                            } else {
                                this.input.resetInputValue();
                                this._updateHint();
                            }
                            this.eventBus.trigger("cursorchange", payload);
                            return true;
                        }
                        return false;
                    },
                    destroy: function destroy() {
                        this.input.destroy();
                        this.menu.destroy();
                    }
                });
            return Typeahead;

            function c(ctx) {
                var methods = [].slice.call(arguments, 1);
                return function() {
                    var args = [].slice.call(arguments);
                    _.each(methods,
                        function(method) {
                            return ctx[method].apply(ctx, args);
                        });
                };
            }
        }();
        (function() {
            "use strict";
            var old, keys, methods;
            old = $.fn.typeahead;
            keys = {
                www: "tt-www",
                attrs: "tt-attrs",
                typeahead: "tt-typeahead"
            };
            methods = {
                initialize: function initialize(o, datasets) {
                    var www;
                    datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
                    o = o || {};
                    www = WWW(o.classNames);
                    return this.each(attach);

                    function attach() {
                        var $input,
                            $wrapper,
                            $hint,
                            $menu,
                            defaultHint,
                            defaultMenu,
                            eventBus,
                            input,
                            menu,
                            typeahead,
                            MenuConstructor;
                        _.each(datasets,
                            function(d) {
                                d.highlight = !!o.highlight;
                            });
                        $input = $(this);
                        $wrapper = $(www.html.wrapper);
                        $hint = $elOrNull(o.hint);
                        $menu = $elOrNull(o.menu);
                        defaultHint = o.hint !== false && !$hint;
                        defaultMenu = o.menu !== false && !$menu;
                        defaultHint && ($hint = buildHintFromInput($input, www));
                        defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
                        $hint && $hint.val("");
                        $input = prepInput($input, www);
                        if (defaultHint || defaultMenu) {
                            $wrapper.css(www.css.wrapper);
                            $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
                            $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null)
                                .append(defaultMenu ? $menu : null);
                        }
                        MenuConstructor = defaultMenu ? DefaultMenu : Menu;
                        eventBus = new EventBus({
                            el: $input
                        });
                        input = new Input({
                                hint: $hint,
                                input: $input
                            },
                            www);
                        menu = new MenuConstructor({
                                node: $menu,
                                datasets: datasets
                            },
                            www);
                        typeahead = new Typeahead({
                                input: input,
                                menu: menu,
                                eventBus: eventBus,
                                minLength: o.minLength
                            },
                            www);
                        $input.data(keys.www, www);
                        $input.data(keys.typeahead, typeahead);
                    }
                },
                isEnabled: function isEnabled() {
                    var enabled;
                    ttEach(this.first(),
                        function(t) {
                            enabled = t.isEnabled();
                        });
                    return enabled;
                },
                enable: function enable() {
                    ttEach(this,
                        function(t) {
                            t.enable();
                        });
                    return this;
                },
                disable: function disable() {
                    ttEach(this,
                        function(t) {
                            t.disable();
                        });
                    return this;
                },
                isActive: function isActive() {
                    var active;
                    ttEach(this.first(),
                        function(t) {
                            active = t.isActive();
                        });
                    return active;
                },
                activate: function activate() {
                    ttEach(this,
                        function(t) {
                            t.activate();
                        });
                    return this;
                },
                deactivate: function deactivate() {
                    ttEach(this,
                        function(t) {
                            t.deactivate();
                        });
                    return this;
                },
                isOpen: function isOpen() {
                    var open;
                    ttEach(this.first(),
                        function(t) {
                            open = t.isOpen();
                        });
                    return open;
                },
                open: function open() {
                    ttEach(this,
                        function(t) {
                            t.open();
                        });
                    return this;
                },
                close: function close() {
                    ttEach(this,
                        function(t) {
                            t.close();
                        });
                    return this;
                },
                select: function select(el) {
                    var success = false, $el = $(el);
                    ttEach(this.first(),
                        function(t) {
                            success = t.select($el);
                        });
                    return success;
                },
                autocomplete: function autocomplete(el) {
                    var success = false, $el = $(el);
                    ttEach(this.first(),
                        function(t) {
                            success = t.autocomplete($el);
                        });
                    return success;
                },
                moveCursor: function moveCursoe(delta) {
                    var success = false;
                    ttEach(this.first(),
                        function(t) {
                            success = t.moveCursor(delta);
                        });
                    return success;
                },
                val: function val(newVal) {
                    var query;
                    if (!arguments.length) {
                        ttEach(this.first(),
                            function(t) {
                                query = t.getVal();
                            });
                        return query;
                    } else {
                        ttEach(this,
                            function(t) {
                                t.setVal(newVal);
                            });
                        return this;
                    }
                },
                destroy: function destroy() {
                    ttEach(this,
                        function(typeahead, $input) {
                            revert($input);
                            typeahead.destroy();
                        });
                    return this;
                }
            };
            $.fn.typeahead = function(method) {
                if (methods[method]) {
                    return methods[method].apply(this, [].slice.call(arguments, 1));
                } else {
                    return methods.initialize.apply(this, arguments);
                }
            };
            $.fn.typeahead.noConflict = function noConflict() {
                $.fn.typeahead = old;
                return this;
            };

            function ttEach($els, fn) {
                $els.each(function() {
                    var $input = $(this), typeahead;
                    (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
                });
            }

            function buildHintFromInput($input, www) {
                return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint)
                    .css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required")
                    .attr({
                        autocomplete: "off",
                        spellcheck: "false",
                        tabindex: -1
                    });
            }

            function prepInput($input, www) {
                $input.data(keys.attrs,
                    {
                        dir: $input.attr("dir"),
                        autocomplete: $input.attr("autocomplete"),
                        spellcheck: $input.attr("spellcheck"),
                        style: $input.attr("style")
                    });
                $input.addClass(www.classes.input).attr({
                    autocomplete: "off",
                    spellcheck: false
                });
                try {
                    !$input.attr("dir") && $input.attr("dir", "auto");
                } catch (e) {
                }
                return $input;
            }

            function getBackgroundStyles($el) {
                return {
                    backgroundAttachment: $el.css("background-attachment"),
                    backgroundClip: $el.css("background-clip"),
                    backgroundColor: $el.css("background-color"),
                    backgroundImage: $el.css("background-image"),
                    backgroundOrigin: $el.css("background-origin"),
                    backgroundPosition: $el.css("background-position"),
                    backgroundRepeat: $el.css("background-repeat"),
                    backgroundSize: $el.css("background-size")
                };
            }

            function revert($input) {
                var www, $wrapper;
                www = $input.data(keys.www);
                $wrapper = $input.parent().filter(www.selectors.wrapper);
                _.each($input.data(keys.attrs),
                    function(val, key) {
                        _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
                    });
                $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr)
                    .removeClass(www.classes.input);
                if ($wrapper.length) {
                    $input.detach().insertAfter($wrapper);
                    $wrapper.remove();
                }
            }

            function $elOrNull(obj) {
                var isValid, $el;
                isValid = _.isJQuery(obj) || _.isElement(obj);
                $el = isValid ? $(obj).first() : [];
                return $el.length ? $el : null;
            }
        })();
    });
